home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / 129_01.zip / 210MODEM.C < prev    next >
Text File  |  1993-06-01  |  25KB  |  819 lines

  1. /************************************************************************/
  2. /*                modem.c                 */
  3. /*                                    */
  4. /*        modem code for Citadel bulletin board system        */
  5. /*    NB: this code is rather machine-dependent:  it will typically    */
  6. /*    need some twiddling for each new installation.            */
  7. /*                  82Nov05 CrT                */
  8. /************************************************************************/
  9.  
  10. /************************************************************************/
  11. /*                history                 */
  12. /*                                    */
  13. /* 85Jan12 HAW    SECONDSFACTOR == secondsfactor now, for portability.    */
  14. /* 84Dec28 HAW    Ensure execution of HANGUP when carrier is lost.    */
  15. /* 84Dec16 JLS&HAW Start installation of new WC protocol downloader.    */
  16. /* 84Aug22 HAW    Compilation directive for 8085 chips inserted.        */
  17. /* 84Jul08 JLS & HAW ReadFile() fixed for the 255 rollover.        */
  18. /* 84Jul03 JLS & HAW All references to putCh changed to putChar.    */
  19. /* 84Jun23 HAW & JLS Local unused variables zapped.            */
  20. /* 84Mar07 HAW    Upgrade to BDS 1.50a begun.                */
  21. /* 83Mar01 CrT    FastIn() ignores LFs etc -- CRLF folks won't be trapped.*/
  22. /* 83Feb25 CrT    Possible fix for backspace-in-message-entry problem.    */
  23. /* 83Feb18 CrT    fastIn() upload mode cutting in on people.  Fixed.    */
  24. /* 82Dec16 dvm    modemInit revised for FDC-1, with kludge for use with    */
  25. /*        Big Board development system                */
  26. /* 82Dec06 CrT    2.00 release.                        */
  27. /* 82Nov15 CrT    readfile() & sendfile() borrowed from TelEdit.c     */
  28. /* 82Nov05 CrT    Individual history file established            */
  29. /************************************************************************/
  30.  
  31. #include <210ctdl.h>
  32.  
  33. /************************************************************************/
  34. /*                Contents                */
  35. /*                                    */
  36. /*    BBSCharReady()        returns true if user input is ready    */
  37. /*    doWC()            startup routine for the WC downloader.    */
  38. /*  #    fastIn()        kludge code compiling other stuff inline*/
  39. /*    getCh()         bottom-level console-input filter    */
  40. /*  #    getMod()        bottom-level modem-input   filter    */
  41. /*    iChar()         top-level user-input function        */
  42. /*    interact()        chat mode                */
  43. /*    interpret()        interprets a configuration routine    */
  44. /*    KBReady()        returns TRUE if a console char is ready */
  45. /*    mOReady()        returns true if modem can accept a char */
  46. /*    modIn()         returns a user char            */
  47. /*    modemInit()        top-level initialize-all-modem-stuff    */
  48. /*    oChar()         top-level user-output function        */
  49. /*  #    outMod()        bottom-level modem output        */
  50. /*    pause()         pauses for N/100 seconds        */
  51. /*  #    putChar()                            */
  52. /*    readFile()        accept a file using WC protocol     */
  53. /*    receive()        read modem char or time out        */
  54. /*    ringSysop()        signal chat-mode request        */
  55. /*    sendWCChar()        Helps chuck output via WC protocol.    */
  56. /*                                    */
  57. /*    # == routines you should certainly check when porting system    */
  58. /************************************************************************/
  59.  
  60. /************************************************************************/
  61. /*        The principal dependencies:                */
  62. /*                                    */
  63. /*  iChar   modIn                    outMod        */
  64. /*        modIn   getMod  getCh   mIReady kBReady outMod  carrDetect    */
  65. /*            getMod                        */
  66. /*                getCh                    */
  67. /*                    mIReady                */
  68. /*                        kBReady            */
  69. /*                                carrDetect    */
  70. /*                                    */
  71. /*  oChar                        outMod        */
  72. /*                            outMod  mOReady    */
  73. /************************************************************************/
  74.  
  75.  
  76. /************************************************************************/
  77. /*    BBSCharReady() returns TRUE if char is available from user    */
  78. /*    NB: user may be on modem, or may be sysop in CONSOLE mode    */
  79. /************************************************************************/
  80. char BBSCharReady()
  81. {
  82.     char KBReady();
  83.  
  84.     return ((haveCarrier       &&   interpret(pMIReady))
  85.      || (whichIO==CONSOLE  &&   KBReady()           )
  86.     );
  87. }
  88.  
  89. /************************************************************************/
  90. /*    doWC() is the initialization routine for WC downloading.  If    */
  91. /*    called with mode == STARTUP, it sets up globals and gets ready    */
  92. /*    for the initial NAK.  If mode == anything else, then it cleans    */
  93. /*    up after the downloader.                    */
  94. /************************************************************************/
  95. doWC(mode)
  96. char mode;        /* See above */
  97. {
  98.     int i, m;
  99.  
  100.     if (mode == STARTUP) {    /* Setup globals for the coming fun.    */
  101.     WCError  = FALSE;
  102.     WCSecNum = 1;
  103.     i     = 0;
  104.     WCChar     = 0;
  105.     while (1) {        /* Get that darn initial NAK        */
  106.         m = receive(MINUTE);
  107.         if (m == CAN || m == ERROR) /* Didn't get initial        */
  108.         return FALSE;        /* NAK, so time out ..        */
  109.         if (m == NAK)        /* There it is!         */
  110.         return TRUE;
  111.         if (++i == ERRORMAX)    /* 10 chars, no NAKs?        */
  112.         return FALSE;        /* Then ferget it.        */
  113.     }
  114.     }
  115.     else {    /* Cleanup after downloader                */
  116.     if (WCError) return;
  117.     for (; WCChar != 0 && sendWCChar(' ');)
  118.         ;                    /* Do final sector    */
  119.     if (WCError) return;
  120.     for (i = 0; i < ERRORMAX; i++) {
  121.         outMod(EOT);
  122.         if (receive(MINUTE) == ACK || !haveCarrier) break;
  123.     }
  124.     }
  125. }
  126.  
  127. /************************************************************************/
  128. /*    fastIn() is  a special kludge to read in text from the modem    */
  129. /*    as quickly as possible, to allow message upload without     */
  130. /*    handshaking.  Hence we hand-compile some other routines inline    */
  131. /*    Called only from getText(), when whichIO==MODEM.        */
  132. /*    Externals:    see below                     */
  133. /*                                    */
  134. /*    This code is probably overkill for 300 baud, but it may handle    */
  135. /*    1200 baud as well.                        */
  136. /*                                    */
  137. /*    code being speed-optimized would normally be:            */
  138. /*                                    */
  139. /*    while (                             */
  140. /*        !(    (c=iChar()) == NEWLINE     &&   buf[i-1] == NEWLINE )    */
  141. /*        &&    i < lim                         */
  142. /*        &&    (haveCarrier || whichIO == CONSOLE)            */
  143. /*    ) {                                */
  144. /*        if (c != BACKSPACE)  buf[i++] = c;                */
  145. /*        else {                            */
  146. /*        /  handle deletes:     /                */
  147. /*        oChar(' ');                        */
  148. /*        oChar(BACKSPACE);                    */
  149. /*        if (i>0  &&  buf[i-1] != NEWLINE)  i--;         */
  150. /*        else                   oChar(BELL);     */
  151. /*        }                                */
  152. /*    }                                */
  153. /************************************************************************/
  154. #define cursor        fpc1        /* external char *        */
  155. #define BUFEND        fpc2        /* external char *        */
  156. #define tryCount    fi1        /* external int         */
  157. #define isFast        fi2        /* external int         */
  158. #define ch        fc1        /* external char        */
  159. #define lastWasNL    fc2        /* external char        */
  160. #define slow        fc3        /* external char        */
  161. fastIn(continuing)
  162. char continuing;    /* TRUE if we are continuing a message    */
  163. {
  164.     char cache, notFinished;
  165.     int  tryStart, shortTime;
  166.  
  167.     isFast    = 0;
  168.  
  169.     tryStart    = 500*megaHz;
  170.     shortTime    = 480*megaHz;
  171.     cursor    = &msgBuf.mbtext[0      ];
  172.     BUFEND    = &msgBuf.mbtext[MAXTEXT-1];
  173.  
  174.     if (continuing)   while (*cursor) ++cursor; /* find where we were    */
  175.  
  176.     notFinished = TRUE;
  177.     lastWasNL    = FALSE;
  178.  
  179.     /* put newline at start of buffer to simplify BACKSPACE check:    */
  180.     cache          = msgBuf.mbtext[-1];
  181.     msgBuf.mbtext[-1]      = NEWLINE;
  182.  
  183.     while (notFinished     &&   cursor < BUFEND) {
  184.     tryCount = tryStart;    /* try to be waiting for each char    */
  185.     while (--tryCount  &&  !interpret(pMIReady));
  186.  
  187.     if (tryCount>shortTime) isFast++;
  188.     else            isFast--;
  189.  
  190.     if (!tryCount) {
  191.         /* no modem char -- take break to check other stuff */
  192.         if (KBReady()) {
  193.         if (getCh() == SPECIAL) {
  194.             mPrintf("\n system is now in CONSOLE mode.\n ");
  195.             whichIO    = CONSOLE;
  196.             notFinished = FALSE;
  197.             setUp(FALSE);
  198.         }
  199.         }
  200.         if (!interpret(pCarrDetect)) {
  201.         modIn();    /* let modIn() announce it etc    */
  202.         notFinished    = FALSE;
  203.         }
  204.     } else {
  205.         /* time to read modem char: */
  206.         switch (ch =  filter[ inp(mData) & 0x7F ]) {
  207.         case NEWLINE:
  208.         if (lastWasNL)    {
  209.             notFinished = FALSE;
  210.         } else {
  211.             lastWasNL    = TRUE;
  212.  
  213.             *cursor++    = ch;
  214.  
  215.             if (isFast>0  ||  !termLF)     ch = '\r';
  216.             else {
  217.             oChar('\r');
  218.             while (!interpret(pMOReady));    /* for outp()    */
  219.             }
  220.         }
  221.         isFast    = 0;    /* figure speed of each line independently */
  222.         break;
  223.         case BACKSPACE:
  224.         /* people don't upload backspaces -- one hopes! --    */
  225.         /* so we don't worry about speed so much here:        */
  226.         if (*--cursor == NEWLINE) {
  227.             /* trying to erase to previous line -- disallow    */
  228.             /* because we have no upline:            */
  229.             ch = BELL;
  230.             cursor++;
  231.         } else {
  232.             oChar(BACKSPACE);
  233.             oChar(' ');     /* erase last char            */
  234.         }
  235.         while (!interpret(pMOReady));        /* for outp()    */
  236.         break;
  237.         case '\0':
  238.         /* ignore unwanted chars completely: */
  239.         break;
  240.         default:
  241.         lastWasNL    = FALSE;
  242.         *cursor++    = ch;
  243.         break;
  244.         }
  245.         /* echo to console--expendable but nice: */
  246.         putChar(ch);   /* Let putChar decide if the console should see it */
  247.         while (!interpret(pMOReady));        /* for outp()   */
  248.         outp(mData, ch);    /* (was:)assume port is ready by now          */
  249.     }
  250.     }
  251.     *cursor        = '\0';     /* tie off message        */
  252.     msgBuf.mbtext[-1]    = cache;    /* return borrowed space    */
  253.  
  254.     if (cursor == BUFEND)
  255.     mPrintf("\n \7BUFFER OVERFLOW\n ");
  256. }
  257.  
  258.  
  259. /************************************************************************/
  260. /*    getCh() reads a console char                    */
  261. /*        In CONSOLE mode, CRs are changed to newlines        */
  262. /*        Rubouts are changed to backspaces                */
  263. /*    Returns:    resulting char                    */
  264. /************************************************************************/
  265. char getCh()
  266. {
  267.     char bios();
  268.  
  269.     return bios(3);
  270. }
  271.  
  272. /************************************************************************/
  273. /*    getMod() is bottom-level modem-input routine            */
  274. /*      kills any parity bit                        */
  275. /*      rubout            -> backspace            */
  276. /*      CR                -> newline            */
  277. /*      other nonprinting chars    -> blank            */
  278. /*    Returns: result                         */
  279. /************************************************************************/
  280. char getMod() {
  281.     char inp();
  282.  
  283.     return inp(mData) & 0x7F;
  284. }
  285.  
  286. /************************************************************************/
  287. /*    iChar() is the top-level user-input function -- this is the    */
  288. /*    function the rest of Citadel uses to obtain user input        */
  289. /************************************************************************/
  290. char iChar()
  291. {
  292.     char modIn();
  293.     char c;
  294.  
  295.     if (justLostCarrier)   return 0;    /* ugly patch    */
  296.  
  297.     c = filter[modIn()];
  298.  
  299.     switch (echo) {
  300.     case BOTH:
  301.     if (haveCarrier) {
  302.         if (c == '\n')    doCR();
  303.         else        outMod(c);
  304.     }
  305.     putChar(c);    /* Let putChar decide if it should go on console */
  306.     break;
  307.     case CALLER:
  308.     if (whichIO == MODEM) {
  309.         if (c == '\n')    doCR();
  310.         else        outMod(c);
  311.     } else {
  312.         putChar(c);
  313.     }
  314.     break;
  315.     }
  316.     return(c);
  317. }
  318.  
  319. /************************************************************************/
  320. /*    interact() allows the sysop to interact directly with        */
  321. /*    whatever is on the modem.                   dvm 9-82 */
  322. /************************************************************************/
  323. interact() {
  324.     char c, lineEcho, lineFeeds, localEcho;
  325.     char getMod(), getCh(), interpret();
  326.  
  327.     printf(" Direct modem-interaction mode\n");
  328.     lineEcho    = getYesNo("Echo to modem"     );
  329.     localEcho    = getYesNo("Echo keyboard"     );
  330.     lineFeeds    = getYesNo("Echo CR as CRLF"   );
  331.     printf("<ESC> to exit\n");
  332.  
  333.     /* incredibly ugly code.  Rethink sometime: */
  334.     while (c!=SPECIAL) {
  335.     c = 0;
  336.  
  337.     if (c=interpret(pMIReady) ) {
  338.         c  = inp(mData) & 0x7F;
  339.         if (c != '\r') c = filter[c];
  340.         if (c != '\r') {
  341.         if (lineEcho)    outMod(c);
  342.         putChar(c);
  343.         } else {
  344.         if (!lineFeeds) {
  345.             if (lineEcho) outMod('\r');
  346.             putChar('\r');
  347.         } else {
  348.             if (lineEcho) {
  349.             outMod('\r');
  350.             outMod('\n');
  351.             }
  352.             putChar('\r');
  353.             putChar('\n');
  354.         }
  355.         }
  356.     }
  357.     if (KBReady()) {
  358.         c  = filter[getCh()];
  359.         if (c != NEWLINE) {
  360.         if (localEcho)    putChar(c);
  361.         outMod(c);
  362.         } else {
  363.         if (!lineFeeds) {
  364.             if (localEcho) putChar('\r');
  365.             outMod('\r');
  366.         } else {
  367.             if (lineEcho) {
  368.             putChar('\r');
  369.             putChar('\n');
  370.             }
  371.             outMod('\r');
  372.             outMod('\n');
  373.         }
  374.         }
  375.     }
  376.     }
  377. }
  378.  
  379. /************************************************************************/
  380. /*    interpret() interprets a configuration routine            */
  381. /*    Returns byte value computed                    */
  382. /************************************************************************/
  383. char interpret(instr)
  384. union {
  385.     char **pp;
  386.     int  *pi;
  387.     char *pc;
  388. } instr;
  389. {
  390.     char inp();
  391.     char accum;     /* our sole accumulator */
  392.     char *prompt;
  393.     int  lowLim, topLim;
  394.  
  395.     while (TRUE) {
  396.     switch (*instr.pc++) {
  397.     case RET:    return accum;                break;
  398.     case RET0:    if (!accum) return 0;            break;
  399.     case RET1:    if (accum) return 1;            break;
  400. case BOUT:    printf("\n%c %d\n", accum, accum); break;
  401.     case ANDI:    accum           &= *instr.pc++;        break;
  402.     case INP:    accum        = inp(*instr.pc++);    break;
  403.     case XORI:    accum           ^= *instr.pc++;        break;
  404.  
  405.     case LOAD:    accum        = *(*instr.pp++);    break;
  406.     case LOADI:    accum        = *instr.pc++;        break;
  407.     case LOADX:    accum        = scratch[*instr.pc++]; break;
  408.     case ORI:    accum           |= *instr.pc++;        break;
  409.     case OUTP:    outp(*instr.pc++, accum);        break;
  410.     case PAUSEI:    pause(*instr.pc++);            break;
  411.     case STORE:    *(*instr.pp++)    = accum;        break;
  412.     case STOREX:    scratch[*instr.pc++]    = accum;    break;
  413.     case OPRNUMBER:
  414.         prompt    = instr.pc;
  415.         while(*instr.pc++);     /* step over prompt    */
  416.         lowLim    = *instr.pc++;
  417.         topLim    = *instr.pc++;
  418.         accum    = getNumber(prompt, lowLim, topLim);
  419.         break;
  420.     case OUTSTRING:
  421.         while(*instr.pc) {
  422.         pause(5);    /* SmartModem can't handle 300 baud    */
  423.         outMod(*instr.pc++);    /* output string */
  424.         }
  425.         instr.pc++;                 /* skip null     */
  426.         break;
  427.     default:
  428.         printf("intrp-no opcod%d", *(instr.pc-1));
  429.         break;
  430.     }
  431.     }
  432. }
  433.  
  434. /************************************************************************/
  435. /*    KBReady() returns TRUE if a console char is ready        */
  436. /************************************************************************/
  437. char KBReady()
  438. {
  439.     char bios();
  440.  
  441.     return bios(2);
  442. }
  443.  
  444. /************************************************************************/
  445. /*    modemInit() is responsible for all modem-related initialization */
  446. /*    at system startup                        */
  447. /*    Globals modified:    haveCarrier    visibleMode        */
  448. /*                whichIO     modStat         */
  449. /*                exitToCpm    justLostCarrier     */
  450. /*    modified 82Dec10 to set FDC-1 SIO-B clock speed at        */
  451. /*    300 baud     -dvm                        */
  452. /************************************************************************/
  453. modemInit()
  454. {
  455.     newCarrier        = FALSE;
  456.     Ooops        = '\n';
  457.     visibleMode     = FALSE;
  458.     exitToCpm        = FALSE;
  459.     justLostCarrier    = FALSE;
  460.  
  461.     whichIO        = CONSOLE;
  462.  
  463. #ifdef FDC-1
  464. #define MONBASE 0xF800
  465. #define STSSP    MONBASE+0x045
  466.     call(STSSP, 0, 0, 0x0B, 0x05);    /* 300 baud on SIO-B */
  467. #endif
  468.  
  469. #ifdef VFC-2
  470.     /* dummy call (to CONSTAT) to make code size equal for both systems */
  471.     call(0xF006, 0, 0, 0x0B, 0x05);
  472. #endif
  473.  
  474.     if (!rcpm) {
  475.     interpret(pInitPort);
  476.     interpret(pHangUp);
  477.     }
  478.     haveCarrier = modStat = interpret(pCarrDetect);
  479. }
  480.  
  481. /************************************************************************/
  482. /* modIn() toplevel modem-input function                */
  483. /*   If DCD status has changed since the last access, reports        */
  484. /*   carrier present or absent and sets flags as appropriate.        */
  485. /*   In case of a carrier loss, waits 20 ticks and rechecks        */
  486. /*   carrier to make sure it was not a temporary glitch.        */
  487. /*   If carrier is newly received, returns newCarrier = TRUE;  if    */
  488. /*   carrier lost returns 0.  If carrier is present and state        */
  489. /*   has not changed, gets a character if present and            */
  490. /*   returns it.  If a character is typed at the console,        */
  491. /*   checks to see if it is keyboard interrupt character.  If        */
  492. /*   so, prints short-form console menu and awaits next         */
  493. /*   keyboard character.                        */
  494. /* Globals modified:    carrierDetect    modStat     haveCarrier    */
  495. /*            justLostCarrier whichIO     exitToCpm    */
  496. /*            visibleMode                    */
  497. /* Returns:    modem or console input character,            */
  498. /*        or above special values                 */
  499. /************************************************************************/
  500. char modIn()
  501. {
  502.     char    getMod(), getCh(), interpret(), KBReady();
  503.     char    c;
  504.     unsigned    hi, lo;
  505.  
  506.     hi    = (HITIMEOUT * megaHz);
  507.     lo    = 0xFF;
  508.     while (TRUE) {
  509.     if ((whichIO==MODEM) && (c=interpret(pCarrDetect)) != modStat) {
  510.         /* carrier changed     */
  511.         if (c)  {       /* carrier present    */
  512.         printf("Carr-detect\n");
  513.         haveCarrier    = TRUE;
  514.         pause(200);
  515.         modStat     = c;
  516.         newCarrier    = TRUE;
  517.         justLostCarrier = FALSE;
  518.         return(0);
  519.         } else {
  520.         pause(200);            /* confirm it's not a glitch */
  521.         if (!interpret(pCarrDetect)) {      /* check again */
  522.             printf("Carr-loss\n");
  523.  
  524.             interpret(pHangUp);
  525.             haveCarrier     = FALSE;
  526.             modStat        = FALSE;
  527.             justLostCarrier = TRUE;
  528.  
  529. /*            while(interpret(pMIReady)) getMod();    eat garbage */
  530.  
  531.             return(0);
  532.         }
  533.         }
  534.     }
  535.  
  536.     if (interpret(pMIReady)) {
  537.         if (haveCarrier) {
  538.         c = getMod();
  539.         if (whichIO == MODEM)    return c;
  540.         }
  541.     }
  542.  
  543.     if (KBReady()) {
  544.         c = getCh();
  545.         if (whichIO == CONSOLE) return(c);
  546.         else {
  547.         if (c == SPECIAL) {
  548.             printf("CONSOLE mode\n ");
  549.             whichIO = CONSOLE;
  550.             setUp(FALSE);
  551.             return 0;
  552.         }
  553.         }
  554.     }
  555.  
  556.     /* check for no input.    (Short-circuit evaluation, remember!) */
  557.     if (whichIO==MODEM  &&    haveCarrier  &&  !--lo    &&  !--hi) {
  558.         mPrintf("Sleeping? Call again :-)");
  559.         interpret(pHangUp);
  560.     }
  561.     }
  562. }
  563.  
  564. /************************************************************************/
  565. /*    oChar() is the top-level user-output function            */
  566. /*      sends to modem port and console both                */
  567. /*      does conversion to upper-case etc as necessary        */
  568. /*      in "debug" mode, converts control chars to uppercase letters    */
  569. /*    Globals modified:    prevChar                */
  570. /************************************************************************/
  571. oChar(c)
  572. char c;
  573. {
  574.     prevChar = c;            /* for end-of-paragraph code    */
  575.     if (outFlag) return;        /* s(kip) mode            */
  576.  
  577.     if (termUpper)    c = toupper(c);
  578.     if (debug)        c = visible(c);
  579.     if (c == NEWLINE)    c = ' ';    /* doCR() handles real newlines */
  580.  
  581.     /* show on console            */
  582.     putChar(c);
  583.  
  584.     if (usingWCprotocol)
  585.     sendWCChar(c);
  586.     else if (haveCarrier)
  587.     outMod(c);
  588. }
  589.  
  590.  
  591. /************************************************************************/
  592. /*    outMod stuffs a char out the modem port             */
  593. /************************************************************************/
  594. outMod(c)
  595. char c;
  596. {
  597.     while(!interpret(pMOReady));
  598.     outp(mData, c);
  599. }
  600.  
  601. /************************************************************************/
  602. /*    pause() busy-waits N/100 seconds                */
  603. /************************************************************************/
  604. pause(i)
  605. int i;
  606. {
  607.     int j;
  608.  
  609.     for (;  i;    i--) {
  610.     for (j=(secondsfactor*megaHz);    j;  j--);
  611.     }
  612. }
  613.  
  614. /************************************************************************/
  615. /*    putChar()                            */
  616. /************************************************************************/
  617. putChar(c)
  618. char c;
  619. {
  620.     if (c != ESC && (echo == BOTH || (whichIO == CONSOLE && echo != NEITHER))) {
  621.     bios(4, c);
  622.     if (c == '\n')
  623.         putChar('\r');
  624.     }
  625. }
  626.  
  627. /************************************************************************/
  628. /*    receive() gets a modem character, or times out ...        */
  629. /*    Returns:    char on success else ERROR            */
  630. /************************************************************************/
  631. int receive(seconds)
  632. int  seconds;
  633. {
  634.     unsigned  count;
  635.  
  636.     count = seconds * 100;
  637.     while (!interpret(pMIReady)  &&  --count)  pause(1);
  638.     if (count)    return inp(mData);
  639.  
  640.     return(ERROR);
  641. }
  642.  
  643. /************************************************************************/
  644. /*    readFile() accepts a file from modem using Ward Christensen's    */
  645. /*    protocol.  (ie, compatable with xModem, modem7, yam, modem2...) */
  646. /*    Returns:    TRUE on successful transfer, else FALSE     */
  647. /************************************************************************/
  648. char readFile(pc)
  649. int  (*pc)();    /* pc will accept the file one character at a time.    */
  650.         /* returns ERROR on any problem, and closes the file    */
  651.         /* when handed ERROR as an argument.            */
  652. {
  653.     int  i, firstchar, lastSector, thisSector, thisComplement, tries;
  654.     int  toterr, checksum;
  655.     char badSector, writeError;
  656.     char sectBuf[SECTSIZE];
  657.     char *nextChar;
  658.  
  659.     if (!getYesNo("Ready for WC transfer"))   return FALSE;
  660.  
  661.     lastSector    = 0;
  662.     tries    = 0;
  663.     toterr    = 0;
  664.     writeError    = FALSE;
  665.  
  666.     while (interpret(pMIReady))   inp(mData);    /* clear garbage    */
  667.  
  668.     printf("Awaiting #0 (Try=0, Errs=0)  \r");
  669.  
  670.     do {
  671.     badSector = FALSE;
  672.  
  673.     /* get synchronized: */
  674.     do {
  675.         firstchar = receive(10);
  676.     } while (
  677.         firstchar != SOH &&
  678.         firstchar != EOT &&
  679.         firstchar != ERROR
  680.     );
  681.  
  682.     if (firstchar == ERROR)   badSector = TRUE;
  683.  
  684.     if (firstchar == SOH)  {
  685.         /* found StartOfHeader -- read sector# in: */
  686.         thisSector        = receive (1);
  687.         thisComplement    = receive (1);    /* 1's comp of thisSector */
  688.  
  689.         if ((thisSector + thisComplement) != 0xFF)    badSector = TRUE;
  690.         else {
  691.         if (thisSector == (lastSector + 1) % 256) {
  692.             /* right sector... let's read it in */
  693.             checksum    = 0;
  694.             nextChar    = sectBuf;
  695.             for (i=SECTSIZE;  i;  i--) {
  696.             *nextChar    = receive (1);
  697.             checksum    = (checksum + *nextChar++) & 0xFF;
  698.             }
  699.  
  700.             if (checksum != receive (1))  badSector = TRUE;
  701.             else {
  702.             tries        = 0;
  703.             lastSector    = thisSector % 256;
  704.  
  705.             printf("Awaiting #%d (Try=0, Errs=%d)  \r",
  706.                 thisSector, toterr
  707.             );
  708.  
  709.             if (tries && toterr) putchar('\n');
  710.  
  711.             /* write sector to where-ever: */
  712.             nextChar = sectBuf;
  713.             for (i=SECTSIZE;  i;  i--) {
  714.                 writeError     &= (*pc)(*nextChar++) == ERROR;
  715.             }
  716.             if (!writeError)  outMod(ACK);
  717.             }
  718.         } else    {
  719.             /* not expected sector... */
  720.             if (thisSector != lastSector)  badSector = TRUE;
  721.             else {
  722.             /* aha -- sender missed an ACK and resent last: */
  723.             do;  while (receive(1) != ERROR);   /* eat it */
  724.             outMod(ACK);    /* back in synch! */
  725.             }
  726.         }
  727.         }    /* end of "if (thisSector + thisComplement == 255"    */
  728.     }    /* end of "if (firstChar == SOH)"            */
  729.  
  730.     if (badSector)    {
  731.         tries++;
  732.         if (lastSector != 0)  toterr++;
  733.  
  734.         while (receive (1) != ERROR);
  735.         printf("Awaiting #%d (Try=%d, Errs=%d)  \r",
  736.         lastSector, tries, toterr
  737.         );
  738.         if (tries && toterr) putchar('\n');
  739.         outMod(NAK);
  740.     }
  741.     } while (
  742.     firstchar != EOT       &&
  743.     tries      <  ERRORMAX  &&
  744.     !writeError
  745.     );
  746.  
  747.     if (firstchar != EOT   ||    tries >= ERRORMAX) {
  748.     printf("\naborting\n");
  749.     return FALSE;
  750.     } else {
  751.     outMod(ACK);
  752.     printf("\nfile reception complete.\n");
  753.     return TRUE;
  754.     }
  755. }
  756.  
  757. /************************************************************************/
  758. /*    ringSysop() signals a chat mode request.  Exits on input from    */
  759. /*  modem or keyboard.                            */
  760. /************************************************************************/
  761. ringSysop()
  762. {
  763.     char BBSCharReady();
  764.     int  i;
  765.  
  766.     whichIO = CONSOLE;
  767.     mPrintf("\n Ringing sysop.\n ");
  768.  
  769.     for (i=0; !BBSCharReady() && !KBReady() && interpret(pCarrDetect);
  770.                                i = ++i % 7)  {
  771.     /* play shave-and-a-haircut/two bits... as best we can: */
  772.     oChar(BELL);
  773.     pause(shave[i]);
  774.     }
  775.  
  776.     if (!interpret(pCarrDetect))
  777.     modIn();            /* Let modIn() announce it    */
  778.  
  779.     if (KBReady())   {
  780.     getCh();
  781.     interact();
  782.     }
  783.     whichIO = MODEM;
  784. }
  785.  
  786. /************************************************************************/
  787. /*    SendWCChar() sends data via WC protocol.            */
  788. /************************************************************************/
  789. sendWCChar(c)
  790. char c;
  791. {
  792.     int  i, j, m;
  793.     char ck;
  794.  
  795.     if (WCError)
  796.     return FALSE;
  797.     WCBuf[WCChar++] = c;        /* Store in buffer        */
  798.     if (WCChar != SECTSIZE)
  799.     return TRUE;
  800.     for (i = 0; i < ERRORMAX; i++) {    /* Time to transmit        */
  801.     outMod(SOH);
  802.     outMod(WCSecNum);
  803.     outMod(~WCSecNum);
  804.     for (j = ck = 0; j < SECTSIZE; j++) {
  805.         outMod(WCBuf[j]);
  806.         ck += WCBuf[j];
  807.     }
  808.     outMod(ck);
  809.     m = receive(MINUTE);
  810.     if (m == ACK || m == CAN || !haveCarrier) break;
  811.     }
  812.     WCChar = 0;
  813.     WCSecNum++;
  814.     if (m == ACK) return TRUE;
  815.     /* else */
  816.     WCError = TRUE;
  817.     return FALSE;        /* Indicates receiver is allergic to us */
  818. }
  819.  if (!interpre